#include    <thread/Exception.h>
#include	<string.h>
#include	<stdio.h>

#include    "Socket.h"

using namespace MyUtil;

InetAddress::InetAddress(uint32_t host, uint16_t port)
{
    _sockaddr.sin_addr.s_addr = host;
    _sockaddr.sin_family = AF_INET;
    _sockaddr.sin_port = htons(port);
}

InetAddress::InetAddress(const string& host, uint16_t port)
{
	_sockaddr.sin_addr.s_addr = InetAddress::toInetHost(host);
    _sockaddr.sin_family = AF_INET;
    _sockaddr.sin_port = htons(port);
}

InetAddress::InetAddress(struct sockaddr& saddr)
{
    memcpy(&_sockaddr, &saddr, sizeof(_sockaddr));
}

InetAddress::InetAddress(struct sockaddr_in& saddrin)
    : _sockaddr(saddrin)
{

}

string 
InetAddress::toStringHost(uint32_t ipnet)
{
    struct in_addr addr;
    addr.s_addr = ipnet;
    return inet_ntoa(addr);
}

string 
InetAddress::toStringHost(struct sockaddr& saddr)
{
    return toStringHost(*((struct sockaddr_in*)&saddr));
}

string
InetAddress::toStringHost(struct sockaddr_in& saddrin)
{
    return toStringHost(saddrin.sin_addr.s_addr);
}

uint32_t 
InetAddress::toInetHost(const string& host)
{
	uint32_t nhost;
    /* First try it as aaa.bbb.ccc.ddd. */
    nhost = inet_addr(host.c_str());
    if (nhost != ULONG_MAX) {
        return nhost;
    }

    struct hostent *hostt;
    if ((hostt = gethostbyname(host.c_str())) == NULL) {
        return 0;
    }
    return (*(struct in_addr *) *hostt->h_addr_list).s_addr;
}

uint32_t 
InetAddress::toInetHost(struct sockaddr& saddr)
{
    return InetAddress::toInetHost(*((struct sockaddr_in*)&saddr));
}

uint32_t 
InetAddress::toInetHost(struct sockaddr_in& saddrin)
{
    return saddrin.sin_addr.s_addr;
}

struct sockaddr_in* 
InetAddress::getSockAddrIn()
{
    return &_sockaddr;
}

struct sockaddr* 
InetAddress::getSockAddr()
{
    return (struct sockaddr*)&_sockaddr;
}

string 
InetAddress::getHostAsString()
{
    return toStringHost(_sockaddr);
}

uint32_t 
InetAddress::getHostAsInt()
{
    return _sockaddr.sin_port;
}

uint16_t 
InetAddress::getPort()
{
    return ntohs(_sockaddr.sin_port);
}

uint16_t 
InetAddress::getInetPort()
{
    return _sockaddr.sin_port;
}

string 
InetAddress::strPort()
{
    char port[10];
    sprintf(port, "%d", ntohs(_sockaddr.sin_port));
    return port;
}

//---------------------------------------------------------------------------
SocketBasic::SocketBasic() 
{
	
}


SocketBasic::SocketBasic(int fd) :
	FileDescriptor(fd)
{
	
}


SocketBasic::~SocketBasic()
{
	
}

int 
SocketBasic::getOption(int key)
{
    int value;
    socklen_t size = sizeof(value);
    if (getsockopt(getFD(), SOL_SOCKET, key, &value, &size) < 0) {
        return -1;
    }
    return value;
}

void
SocketBasic::setOption(int option, int value)
{
    if (setsockopt(_fd, SOL_SOCKET, option, &value, sizeof(value)) < 0) {
    	cout << "Socket::setOption failed" << endl;
    }
}

void 
SocketBasic::setNonBlock(bool nonBlock)
{
    int flags;
    
    /* Get socket block status */
    if ((flags = fcntl(getFD(), F_GETFL, 0)) < 0) {
        /* Handle error */
        cout << "Socket::setBlock " << strerror(errno) << endl;
        return;
    }
    
    if (nonBlock) {
        /* NoBlocking */
        if ((flags = fcntl(getFD(), F_SETFL, flags | O_NONBLOCK)) < 0) {
	        /* Handle error */
	        cout << "Socket::setBlock " << strerror(errno) << endl;
        }
    } else {        
        /* Blocking */
        if ((flags = fcntl(getFD(), F_SETFL, flags & (~O_NONBLOCK))) < 0) {
	        /* Handle error */
	        cout << "Socket::setBlock " << strerror(errno) << endl;          
        }
    }
}

/**
 * @brief 
 * @return
 */
void  SocketBasic::setLinger ( bool isLinger )
{
	struct linger linger;
	memset( &linger, 0, sizeof(linger) );
	linger.l_onoff = isLinger ? 1 : 0;
	setsockopt( getFD(), SOL_SOCKET, SO_LINGER, &linger, sizeof(linger) );
    return ;
}		

//---------------------------------------------------------------------------
Socket::Socket(int fd) :
	SocketBasic(fd)
{
	
}


Socket::Socket(const string& host, int port)
{
    /* step1: create a socket */
    if ((_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    	cout << "Socket::Socket " << strerror(errno) << endl;
	KAKA_THROW(KAKA_IO_ERROR, "");
    }
   
    setOption(SO_REUSEADDR, 1);
  	setOption(SO_KEEPALIVE, 1);
    
    InetAddress inetAddr(host, port);
    if (::connect(_fd, inetAddr.getSockAddr(), sizeof(struct sockaddr)) < 0) {
    	cout << "Socket::Socket " << strerror(errno) << endl;
	KAKA_THROW(KAKA_IO_ERROR, "");
    }
}

Socket::~Socket()
{
//	cout << "Socket::~Socket() " << endl;	
}

string 
Socket::getLocalHost()
{
    struct sockaddr addr;
    socklen_t len = sizeof(struct sockaddr);
    getsockname(_fd, (struct sockaddr*)&addr, &len);

    return InetAddress::toStringHost(addr);
}

string 
Socket::getRemoteHost()
{
    struct sockaddr addr;
    socklen_t len = sizeof(struct sockaddr);
    getpeername(_fd, (struct sockaddr*)&addr, &len);

    return InetAddress::toStringHost(addr);
}

uint16_t 
Socket::getLocalPort()
{
    struct sockaddr addr;
    socklen_t len = sizeof(struct sockaddr);
    getsockname(_fd, (struct sockaddr*)&addr, &len);

    return ntohs(((struct sockaddr_in*)&addr)->sin_port);
}

uint16_t 
Socket::getRemotePort()
{
    struct sockaddr addr;
    socklen_t len = sizeof(struct sockaddr);
    getpeername(_fd, (struct sockaddr*)&addr, &len);

    return ntohs(((struct sockaddr_in*)&addr)->sin_port);
}

//---------------------------------------------------------------------------
ServerSocket::ServerSocket(int port, int num)
{
	/* step1: create a socket */
    if ((_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    	cout << "ServerSocket::ServerSocket " << strerror(errno) << endl;
        throw;
    }

    setOption(SO_REUSEADDR, 1);
  	setOption(SO_KEEPALIVE, 1);
  	
	if (::bind(_fd, InetAddress(INADDR_ANY, port).getSockAddr(), 
		sizeof(struct sockaddr)) < 0) {
		cout << "ServerSocket::ServerSocket " << strerror(errno) << endl;
  		throw;
  	}
    if (::listen (_fd, num) < 0) {
    	cout << "ServerSocket::ServerSocket " << strerror(errno) << endl;
  		throw;
  	}
}

ServerSocket::~ServerSocket(void)
{
  // No body
}

SocketPtr
ServerSocket::accept()
{
	int fd;
    while ((fd = ::accept(_fd, NULL, NULL)) < 0) {
        if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {   // Either a real error occured, or block was interrupted for
            // some reason.  Only abort execution if a real error occured.
            // EINTR is 'interruped system call' error
            // don't return - do the accept again
			cout << " acception error " << endl;
            continue;
        }
        return 0;
    }
//	cout << "acception new fd=" << fd << endl;
    
    return new Socket(fd);
}
